/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.repository.dac;

import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.authority.AuthorityInfo;
import com.inspur.edp.cef.api.repository.DbParameter;
import com.inspur.edp.cef.api.repository.GspDbType;
import com.inspur.edp.cef.api.repository.RefColumnInfo;
import com.inspur.edp.cef.api.repository.TableNameRefContext;
import com.inspur.edp.cef.api.repository.adaptor.IRepositoryAdaptor;
import com.inspur.edp.cef.entity.UQConstraintMediate;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.changeset.Tuple;
import com.inspur.edp.cef.entity.condition.EntityFilter;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.entity.entity.IChildEntityData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.repository.DataSaveParameter;
import com.inspur.edp.cef.entity.repository.DataSaveResult;
import com.inspur.edp.cef.repository.adaptor.EntityRelationalAdaptor;
import com.inspur.edp.cef.repository.assembler.AbstractDataAdapterExtendInfo;
import com.inspur.edp.cef.repository.exception.CefRepositoryException;
import io.iec.edp.caf.databaseobject.api.entity.TempTableContext;

import java.sql.SQLException;
import java.util.*;

public abstract class EntityDac extends BaseRepositoryDac {

    private static List<ChildEntityDac> defaultChildDacList=new ArrayList<>();
    private HashMap<String, ChildEntityDac> childDacCollection;

    ///#region VirtualInfos
    protected boolean getUseDataCache() {
        return false;
    }

    ///#endregion

    protected String getCacheConfigID() {
        return "";
    }

    public EntityRelationalAdaptor getEntityAdaptor() {
        return (EntityRelationalAdaptor) getAdaptor();
    }

    public final List<IEntityData> query(EntityFilter filter, ArrayList<AuthorityInfo> authorities) {
        if(this instanceof ChildEntityDac){//子表查询，不带权限
            authorities = null;
        }
        return getEntityAdaptor().query(filter, authorities);
    }

    public final List<ChildEntityDac> innerGetChildDacList() {
        List<ChildEntityDac> childs = getChildDacList();
        if(childs == null)
            return childs;
        for (ChildEntityDac child : childs) {
            if(getPars() != null)
                child.initParams(getPars());
            initChildExtendInfo(child);
        }
        if(extendInfos != null) {
            for (AbstractDataAdapterExtendInfo extendInfo : extendInfos) {
                Map<String, ChildEntityDac> extChilds = extendInfo.getChildDacList(this);
                if(extChilds == null){
                    continue;
                }
                extChilds.forEach((key,value) -> value.initParams(getPars()));
                childs.addAll(extChilds.values());
            }
        }
        return childs;
    }

    private void initChildExtendInfo(ChildEntityDac child){
        if(extendInfos == null || extendInfos.size()<1)
            return;
        String childNodeCode = child.getNodeCode();
        for(AbstractDataAdapterExtendInfo extendInfo :extendInfos){
            if(extendInfo.getChildExtendInfos().containsKey(childNodeCode))
                child.extendInfos.add(extendInfo.getChildExtendInfos().get(childNodeCode));
        }
    }
    public final ChildEntityDac getChildEntityDac(String nodeCode) {
        if (childDacCollection == null) {
            List<ChildEntityDac> childs = innerGetChildDacList();
            childDacCollection = new HashMap<String, ChildEntityDac>();
            for (ChildEntityDac child :
                    childs) {
                childDacCollection.put(child.getNodeCode(), child);
            }
        }

        if (childDacCollection.containsKey(nodeCode)) {
            return childDacCollection.get(nodeCode);
        }
        throw new CefRepositoryException("未找到子节点" + nodeCode + "的Dac");
    }

    public com.inspur.edp.cef.entity.entity.ICefData createInstance(){
        return null;
    }
    protected  List<ChildEntityDac> getChildDacList()
    {
        return new ArrayList<>();
    }

    public abstract String getNodeCode();


    ///#region retrieve


    protected final void mergeChildDatas(String nodeCode, List<IEntityData> currentDatas, Map<String, List<IChildEntityData>> childDatas) {
        DataValidator.checkForNullReference(childDatas, "childDatas");
        for (IEntityData currentData : currentDatas) {
            List<IChildEntityData> childData = childDatas.get(currentData.getID());
            if (childData == null)
                continue;
            mergeChildData(currentData, nodeCode, childData);
        }
    }

    protected final void mergeChildData(IEntityData data, String childNodeCode, List<IChildEntityData> childData) {

        data.mergeChildData(childNodeCode, convertToEntityDataList(childData));
    }

    protected ArrayList<IEntityData> convertToEntityDataList(List<IChildEntityData> childDatas) {
        if (childDatas == null)
            return null;
        ArrayList<IEntityData> datas = new ArrayList<IEntityData>();
        for (IChildEntityData childData : childDatas) {
            datas.add(childData);
        }
        return datas;
    }

    public final DataSaveResult delete(String id, DataSaveParameter par) throws SQLException {
        DataValidator.checkForEmptyString(id, "id");
        DataSaveResult result = new DataSaveResult();
        List<DbParameter> parameters = new ArrayList<>();
        parameters.add(getEntityAdaptor().buildParam("ID", getEntityAdaptor().getContainColumns().getPrimaryKey().getColumnType(), id));
        deleteChildren("", getEntityAdaptor().buildDeleteByIDFilter(), parameters);
        result.setAffectedRowCount(getEntityAdaptor().delete(id, par));
        if (getUseDataCache()) {
            //Java版临时屏蔽
//			EntityDataCacheService.getInstance().RemoveDataFromCache(getCacheConfigID(), id);
        }

        return result;
    }

    public final void generateTable() {
        getEntityAdaptor().generateTable();
    }

    public TempTableContext createTempTable()
    {
        return getEntityAdaptor().createTempTable();
    }

    public void dropTempTable(TempTableContext tempTableContext)
    {
        getEntityAdaptor().dropTempTable(tempTableContext);
    }

    public final void dropTable() {
        getEntityAdaptor().dropTable();
    }

    public final void checkUniqueness(ArrayList<UQConstraintMediate> constraintMediates) {
        checkUniqueness(constraintMediates, true);
    }

    public final void checkUniqueness(ArrayList<UQConstraintMediate> constraintMediates, boolean throwEx) {
        getEntityAdaptor().checkUniqueness(constraintMediates, throwEx);
    }



    public final void delete(List<String> ids, DataSaveParameter par) throws SQLException {
        deleteChildren("", getEntityAdaptor().getInFilter(ids), null);
        getEntityAdaptor().delete(ids, par);
    }

    public void deleteChildren(String joinInfo, String filter, List<DbParameter> dbPars) throws SQLException {
        List<ChildEntityDac> childDacList = innerGetChildDacList();
        if (childDacList != null) {
            for (ChildEntityDac childDac : childDacList) {
                List<DbParameter> clonePars = new ArrayList<>();
                for(DbParameter par: dbPars){
                    DbParameter clonePar=new DbParameter(par.getParamName(), par.getDataType(), par.getValue());
                    clonePars.add(clonePar);
                }
                childDac.deleteByParent(joinInfo, filter, clonePars);
            }
        }
    }

    ///#endregion

    public final void save(IChangeDetail[] changes) throws SQLException {
        DataValidator.checkForNullReference(changes, "changes");
        try(DacSaveContext ctx = new DacSaveContext(getDbType())) {
            for (IChangeDetail change : changes) {
                saveCore(change, DataSaveParameter.Empty, ctx);
            }
            ctx.getJDBCExecutor().flushBatch();
        } catch (Exception e) {
            throw new CefRepositoryException("", e);
        }
    }
    public List<DataSaveResult> save(List<Tuple<IChangeDetail, DataSaveParameter>> changes) throws SQLException {
        Objects.requireNonNull(changes, "changes");

        List<DataSaveResult> rez = new ArrayList<>(changes.size());
        try(DacSaveContext ctx = new DacSaveContext(getDbType())) {
            for(Tuple<IChangeDetail, DataSaveParameter> tuple : changes) {
                rez.add(saveCore(tuple.getItem1(), tuple.getItem2(), ctx));
            }
            ctx.getJDBCExecutor().flushBatch(ctx.getNodesInfo());
        } catch (Exception e) {
            throw new CefRepositoryException("数据更新失败!内部异常信息："+e.getMessage()+"。",e);
        }
        return rez;
    }

    DataSaveResult saveCore(IChangeDetail change, DataSaveParameter par, DacSaveContext ctx)
            throws SQLException {
        switch (change.getChangeType()) {
            case Added:
                return insert(((AddChangeDetail) change).getEntityData(), par, ctx);
            case Deleted:
                return delete(change.getDataID(), par);
            case Modify:
                return modify((ModifyChangeDetail) change, par, ctx);
            default:
                throw new UnsupportedOperationException();
        }
    }

    public DataSaveResult insert(IEntityData data, DataSaveParameter par, DacSaveContext ctx)
            throws SQLException {
        DataSaveResult result = new DataSaveResult();
        if(this instanceof MainEntityDac){//只有主表走这个
            ctx.addNodeInfo(this.getEntityAdaptor().innerGetTableName());
        }
        result.setAffectedRowCount(getEntityAdaptor().insert(data, par, ctx));
        Map<String, IEntityDataCollection> childs = data.getChilds();
        if (childs == null || childs.isEmpty()) {
            return result;
        }

        for (ChildEntityDac childDac : innerGetChildDacList()) {
            ctx.addNodeInfo(this.getEntityAdaptor().innerGetTableName(), childDac.getEntityAdaptor().innerGetTableName());
            if (childs.containsKey(childDac.getNodeCode()) == false)
                continue;
            IEntityDataCollection childDatas = childs.get(childDac.getNodeCode());
            if (childDatas == null)
                continue;


            for (IEntityData childData : childDatas) {
                DataSaveResult childResult = childDac.insert(childData, par, ctx);
                result.addChildResult(childDac.getNodeCode(),childData.getID(), childResult);
            }
        }
        return result;
    }

    public DataSaveResult modify(ModifyChangeDetail change, DataSaveParameter par,
                                 DacSaveContext ctx) throws SQLException {
        DataSaveResult result = new DataSaveResult();
        if(this instanceof MainEntityDac){
            ctx.addNodeInfo(this.getEntityAdaptor().innerGetTableName());
        }
        if (change.getChildChanges() != null) {
            for (ChildEntityDac childDac : innerGetChildDacList()) {
                ctx.addNodeInfo(this.getEntityAdaptor().innerGetTableName(), childDac.getEntityAdaptor().innerGetTableName());
                Map<String, IChangeDetail> childChanges = change.getChildChanges().get(childDac.getNodeCode());
                if (childChanges == null)
                    continue;
                for (IChangeDetail childChange : childChanges.values()) {
                    DataSaveResult childResult = childDac.saveCore(childChange, par, ctx);
                    result.addChildResult(childDac.getNodeCode(), childChange.getDataID(),
                            childResult);
                }
            }
        }
        result.setAffectedRowCount(getEntityAdaptor().modify(change, par, ctx));
        if (getUseDataCache()) {
            //Java版临时屏蔽
//			EntityDataCacheService.getInstance().RemoveDataFromCache(getCacheConfigID(), change.getID());
        }
        return result;
    }

//
//        public override void initParams(IDictionary<string, object> pars)
//        {
//            base.initParams(pars);
//            foreach(var childDac in getChildDacList())
//            {
//                childDac.initParams(pars);
//            }
//        }
//

    public final String getTableNameByColumns(HashMap<String, String> columns, String keyColumnName, RefObject<String> keyDbColumnName, ArrayList<String> tableAlias) {
        IRepositoryAdaptor adaptor = getAdaptor();
        return adaptor.getTableNameByColumns(columns, keyColumnName, keyDbColumnName, tableAlias);
    }

    public final String getTableNameByRefColumns(HashMap<String, RefColumnInfo> columns, String keyColumnName, RefObject<String> keyDbColumnName, ArrayList<String> tableAlias) {
        IRepositoryAdaptor adaptor = getAdaptor();
        return adaptor.getTableNameByRefColumns(columns, keyColumnName, keyDbColumnName, tableAlias);
    }

    public final String getTableNameByRefColumns(TableNameRefContext tableNameRefContext) {
        IRepositoryAdaptor adaptor = getAdaptor();
        return adaptor.getTableNameByRefColumns(tableNameRefContext);
    }

    public final boolean isDatasEffective(ArrayList<String> dataIds, RefObject<ArrayList<String>> missingDataIds) {
        if (getUseDataCache()) {
            return isDatasEffectiveByCache(dataIds, missingDataIds);
        }
        return isDatasEffectiveWithoutCache(dataIds, missingDataIds);
    }

    public final boolean isDatasEffective(ArrayList<String> dataIds, RefObject<ArrayList<String>> missingDataIds, String propName) {
        if (getUseDataCache()) {
            return isDatasEffectiveByCache(dataIds, missingDataIds);
        }
        return isDatasEffectiveWithoutCache(dataIds, missingDataIds, propName);
    }

    @Deprecated
    public final   boolean isRef( String dataId, String propertyName)
    {
        return getEntityAdaptor().isRef(dataId,propertyName);
    }

    public final boolean isRef( String[] dataIds, String propertyName) {
        return getEntityAdaptor().isRef(dataIds,propertyName);
    }
    public boolean isDatasEffectiveByCache(ArrayList<String> dataIds, RefObject<ArrayList<String>> missingDataIds) {
        return false;
//		EntityDataCacheService.getInstance().getDatas(getCacheConfigID(), dataIds, missingDataIds);
//		return (missingDataIds.argvalue == null || missingDataIds.argvalue.isEmpty());
    }

    public final boolean isDatasEffectiveWithoutCache(ArrayList<String> dataIds, RefObject<ArrayList<String>> missingDataIds) {
        return getEntityAdaptor().isDatasEffective(dataIds, missingDataIds);
    }

    public final boolean isDatasEffectiveWithoutCache(ArrayList<String> dataIds, RefObject<ArrayList<String>> missingDataIds, String propName) {
        return getEntityAdaptor().isDatasEffective(dataIds, missingDataIds, propName);
    }

    public GspDbType getGspDbtype(){
        return getDbType();
    }

    public List<String> getDistinctFJM(String fjnPropertyName, EntityFilter filter,ArrayList<AuthorityInfo> authorityInfos,Integer parentLayer) {
        return getEntityAdaptor().getDistinctFJM(fjnPropertyName,filter,authorityInfos,parentLayer);
    }
}
